package cc.shacocloud.mirage.core;

import cc.shacocloud.mirage.utils.ResolvableType;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Predicate;

/**
 * {@link ApplicationEventMulticaster} 的默认实现
 *
 * @author 思追(shaco)
 */
public class ApplicationEventMulticasterImpl implements ApplicationEventMulticaster {
    
    private final List<ApplicationListener<?>> listenerList = new ArrayList<>();
    
    private final Map<CacheKey, List<ApplicationListener<?>>> cacheKeyListMap = new HashMap<>();
    
    private final StampedLock sl = new StampedLock();
    
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        long stamp = sl.writeLock();
        try {
            listenerList.add(listener);
            cacheKeyListMap.clear();
        } finally {
            sl.unlock(stamp);
        }
    }
    
    @Override
    public void addApplicationListeners(List<ApplicationListener<?>> listeners) {
        long stamp = sl.writeLock();
        try {
            listenerList.addAll(listeners);
            cacheKeyListMap.clear();
        } finally {
            sl.unlock(stamp);
        }
    }
    
    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        long stamp = sl.writeLock();
        try {
            listenerList.remove(listener);
            cacheKeyListMap.clear();
        } finally {
            sl.unlock(stamp);
        }
    }
    
    @Override
    public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {
        long stamp = sl.writeLock();
        try {
            listenerList.removeIf(predicate);
            cacheKeyListMap.clear();
        } finally {
            sl.unlock(stamp);
        }
    }
    
    @Override
    public void removeAllListeners() {
        long stamp = sl.writeLock();
        try {
            listenerList.clear();
            cacheKeyListMap.clear();
        } finally {
            sl.unlock(stamp);
        }
    }
    
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public <E extends ApplicationEvent> CompositeFuture publishEvent(E event) {
        // 获取事件监听器
        List<ApplicationListener<?>> listeners = getListeners(event);
        
        List<Future> futures = new ArrayList<>();
        for (ApplicationListener listener : listeners) {
            Future future = listener.onApplicationEvent(event);
            futures.add(future);
        }
        
        return CompositeFuture.join(futures);
    }
    
    /**
     * 基于应用事件类型获取应用监听器
     *
     * @param event 事件对象
     * @return 关联的监听器 {@link List<ApplicationListener>}
     */
    protected List<ApplicationListener<?>> getListeners(@NotNull ApplicationEvent event) {
        List<ApplicationListener<?>> listeners;
        CacheKey cacheKey = new CacheKey(event.getClass());
        
        // 先尝试乐观读取
        long optimisticReadStamp = sl.tryOptimisticRead();
        listeners = cacheKeyListMap.get(cacheKey);
        if (sl.validate(optimisticReadStamp)) {
            if (Objects.nonNull(listeners)) {
                return listeners;
            }
        }
        
        long stamp = sl.readLock();
        try {
            // 如果过时了，再读一遍
            if (!sl.validate(optimisticReadStamp)) {
                listeners = cacheKeyListMap.get(cacheKey);
                if (Objects.nonNull(listeners)) {
                    return listeners;
                }
            }
            
            long ws = sl.tryConvertToWriteLock(stamp);
            
            // 无法将读锁定转换为写锁定
            if (0L == ws) {
                sl.unlockRead(stamp);
                stamp = sl.writeLock();
                
                // 试着再读一遍
                listeners = cacheKeyListMap.get(cacheKey);
                if (Objects.nonNull(listeners)) {
                    return listeners;
                }
            } else {
                stamp = ws;
            }
            
            // 筛选筛选满足条件的监听器，基于类型是否可用注入进行判断
            listeners = new ArrayList<>();
            
            ResolvableType eventType = ResolvableType.forClass(event.getClass());
            for (ApplicationListener<?> applicationListener : listenerList) {
                ResolvableType generic = ResolvableType.forClass(ApplicationListener.class, applicationListener.getClass())
                        .getGeneric(0);
                
                if (generic.isAssignableFrom(eventType)) {
                    listeners.add(applicationListener);
                }
            }
            
            cacheKeyListMap.put(cacheKey, listeners);
            
            return listeners;
        } finally {
            sl.unlock(stamp);
        }
    }
    
    @RequiredArgsConstructor
    public static class CacheKey {
        private final Class<? extends ApplicationEvent> eventClass;
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            CacheKey cacheKey = (CacheKey) o;
            return Objects.equals(eventClass, cacheKey.eventClass);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(eventClass);
        }
    }
}
